#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "graph.h"

/*
#define VERBOSE
*/

#define LINEMAX 4096

/* scan a single line and verify that it has the right number of fields */
#define PARSE_LINE(numcnt, scancmd, errorstr)\
do {\
if ((numcnt) != (scancmd))\
{\
fprintf(stderr, errorstr);\
exit(1);\
}\
} while(0)


Graph_t * parse_model(char * model_fname)
{
  FILE * fp;
  char buffer[LINEMAX];
  Graph_t * p_graph;
  int f, v;

  fp = fopen(model_fname, "r");

  /*
   * READ THE PREAMBLE 
   */
  
  /* first read the type of graph */
  PARSE_LINE(1, fscanf(fp, "%s\n", buffer),
             "Error: expecting exactly 1 string on line 1\n");
  
  if (strncmp(buffer, "MARKOV", 6))
  {
    fprintf(stderr, "Error: only MARKOV networks are supported\n");
    exit(1);
  }

  p_graph = (Graph_t *) calloc(1, sizeof(*p_graph));
  
  /* read number of variables */
  PARSE_LINE(1, fscanf(fp, "%d\n", &p_graph->graph_num_vars),
             "Error: bad number of variables\n");

  /* allocate memory for the variables */
  p_graph->graph_p_vars = (Variable_t *) calloc(p_graph->graph_num_vars,
                                              sizeof(*p_graph->graph_p_vars));
  
  /* read the cardinality and initialize all the variables */
  for (v=0; v<p_graph->graph_num_vars; v++)
  {
    int card;
    Variable_t * p_var;
    
    p_var = p_graph->graph_p_vars + v;
    
    PARSE_LINE(1, fscanf(fp, "%d ", &card),
               "Error: bad cardinality of variable\n");

    if (card > MAX_CARD)
    {
      fprintf(stderr, "Error: Variable %d has cardinality %d!\n",
              v, card);
      exit(1);
    }
    
    p_var->var_id = v;
    p_var->var_card = card;
    p_var->var_num_facs = 0;
    p_var->var_p_facs = NULL;
  }

  /* read the number of factors */
  PARSE_LINE(1, fscanf(fp, "%d\n", &p_graph->graph_num_facs),
             "Error: bad number of factors\n");

  /* allocate memory for the factors */
  p_graph->graph_p_facs = (Factor_t *) calloc(p_graph->graph_num_facs, 
                                              sizeof(*p_graph->graph_p_facs));
  
  /* read the variables in each factor and initialize the factors
   * along the way compute the number of factors that each variable
   * refers to */
  for (f=0; f<p_graph->graph_num_facs; f++)
  {
    Factor_t * p_fac;
    
    p_fac = p_graph->graph_p_facs + f;

    p_fac->fac_id = f;

    PARSE_LINE(1, fscanf(fp, "%d ", &p_fac->fac_num_vars),
               "Error: incorrect number of variables in factor\n");
    
    p_fac->fac_p_vars = (Variable_t **)calloc(p_fac->fac_num_vars,
                                              sizeof(*p_fac->fac_p_vars));
    
    for (v=0; v<p_fac->fac_num_vars; v++)
    {
      int var_id;
    
      PARSE_LINE(1, fscanf(fp, "%d ", &var_id),
                 "Error: bad variable in clique\n");

      if (var_id >= p_graph->graph_num_vars)
      {
        fprintf(stderr, "Error: clique variable not found\n");
        exit(1);
      }
      
      p_fac->fac_p_vars[v] = p_graph->graph_p_vars + var_id;

      p_fac->fac_p_vars[v]->var_num_facs ++;
    }
  }

  /* now, allocate memory for each variable's list of factors */
  for (v=0; v<p_graph->graph_num_vars; v++)
  {
    Variable_t * p_var;
    
    p_var = p_graph->graph_p_vars + v;
    
    if (!p_var->var_num_facs)
    {
      fprintf(stderr, "Warning: variable %d doesn't appear in any factor\n",
              p_var->var_id);
    }
    else
    {
      p_var->var_p_facs = (Factor_t **) calloc(p_var->var_num_facs,
                                               sizeof(*p_var->var_p_facs));
      /* set the number of variables to zero so that in the subsequent
       * pass through the factors we can increment the number of
       * factors as we see them */
      p_var->var_num_facs = 0;
    }
  }

  /* again go through the factors and this time store a reference from the
   * variables to their factors */
  for (f=0; f<p_graph->graph_num_facs; f++)
  {
    Factor_t * p_fac;
    
    p_fac = p_graph->graph_p_facs + f;

    for (v=0; v<p_fac->fac_num_vars; v++)
    {
      Variable_t * p_var;
      p_var = p_fac->fac_p_vars[v];
      
      p_var->var_p_facs[p_var->var_num_facs ++] = p_fac;
    }
  }

  PARSE_LINE(0, fscanf(fp, "\n"),
             "Error: expecting empty line after preamble\n");
  
  /*
   * FUNCTION TABLES
   */

  /* now, read the function tables */
  for(f=0; f<p_graph->graph_num_facs; f++)
  {
    Factor_t * p_fac;
    int exp_num_entries;                     /* expected number of entries */
    int e;

    p_fac = p_graph->graph_p_facs + f;
    
    /* compute the expected number of entries */
    exp_num_entries = 1;
    for (v=0; v<p_fac->fac_num_vars; v++)
      exp_num_entries *= p_fac->fac_p_vars[v]->var_card;
    
    /* read the actual number of entries */
    PARSE_LINE(1, fscanf(fp, "%d\n", &p_fac->fac_num_entries),
               "Error: expecting number of entries for a function\n");

    if (exp_num_entries != p_fac->fac_num_entries)
      fprintf(stderr, "Error: factor %d expecting %d entries, but got %d\n",
              p_fac->fac_id, exp_num_entries, p_fac->fac_num_entries);

    p_fac->fac_p_entries = (double *)calloc(p_fac->fac_num_entries,
                                            sizeof(*p_fac->fac_p_entries));
    p_fac->fac_p_log_entries = (double *)calloc(p_fac->fac_num_entries,
                                            sizeof(*p_fac->fac_p_log_entries));
    
    /* read each entry */
    for (e=0; e<p_fac->fac_num_entries; e++)
    {
      PARSE_LINE(1, fscanf(fp, "%lf ", p_fac->fac_p_entries + e),
                 "Error: expecting an entry for a function table\n");
      p_fac->fac_p_log_entries[e] = log(p_fac->fac_p_entries[e]);
    }

    /* read a blank line */
    PARSE_LINE(0, fscanf(fp, "\n"),
               "Error: expecting empty line after a function table\n");
  }
  
  fclose(fp);

  printf("Graph has %d variables and %d functions\n",
         p_graph->graph_num_vars, p_graph->graph_num_facs);

#ifdef VERBOSE

  /* print variables */
  for (v=0; v<p_graph->graph_num_vars; v++)
  {
    Variable_t * p_var;

    p_var = p_graph->graph_p_vars + v;

    printf("Var %2d -- facs:", p_var->var_id);

    for (f=0; f<p_var->var_num_facs; f++)
    {
      Factor_t * p_fac;
    
      p_fac = p_var->var_p_facs[f];

      printf(" %d", p_fac->fac_id);
    }

    printf("\n");
  }

  /* print factors */
  for (f=0; f<p_graph->graph_num_facs; f++)
  {
    Factor_t * p_fac;
    int e;
    
    p_fac = p_graph->graph_p_facs + f;
    
    printf("Fac %2d -- vars:", p_fac->fac_id);

    for (v=0; v<p_fac->fac_num_vars; v++)
    {
      Variable_t * p_var;
      p_var = p_fac->fac_p_vars[v];

      printf(" %d", p_var->var_id);
    }

    printf("\n");

    printf("Fac %2d -- table:", p_fac->fac_id);
    for (e=0; e<p_fac->fac_num_entries; e++)
      printf(" %g", p_fac->fac_p_entries[e]);

    printf("\n");
  }

#endif /* VERBOSE */

  /* learn the neighborhood structure of the graph */
  graph_deduce_neighbors(p_graph);
  
  return p_graph;
}


void parse_evidence(char * evidence_fname, Graph_t * p_graph,
                    int * p_num_inst, Instance_t ** pp_inst)
{
  FILE * fp;
  int i;
  int no_evidence;
  
  fp = fopen(evidence_fname, "r");
  
  /* read the number of evidence instances */
  PARSE_LINE(1, fscanf(fp, "%d\n", p_num_inst),
             "Error: expecting the number of evidence instances on line 1\n");

  if (*p_num_inst == 0)
  {
    no_evidence = 1;
    *p_num_inst = 1;
  }
  else
    no_evidence = 0;
  
  /* allocate memory for the instances */
  *pp_inst = (Instance_t *) calloc(*p_num_inst, sizeof(**pp_inst));
  
  /* initialize and read each instance */
  for (i=0; i<*p_num_inst; i++)
  {
    Instance_t * p_inst;
    int inst_num_vars;
    int v;
    
    p_inst = (*pp_inst) + i;
    
    p_inst->inst_p_is_obs = (int *)calloc(p_graph->graph_num_vars,
                                          sizeof(*p_inst->inst_p_is_obs));
    
    p_inst->inst_p_val = (int *)calloc(p_graph->graph_num_vars,
                                       sizeof(*p_inst->inst_p_val));
  
    if (no_evidence)
      break;
    
    /* read the number of variables in the instance */
    PARSE_LINE(1, fscanf(fp, "%d ", &inst_num_vars),
               "Error: expecting the number of variables in instance\n");
  
    for (v=0; v<inst_num_vars; v++)
    {
      int var_id;
      int var_value;

      PARSE_LINE(2, fscanf(fp, "%d %d ", &var_id, &var_value),
                 "Error: expecting the variable id and its value\n");

      p_inst->inst_p_is_obs[var_id] = 1;
      p_inst->inst_p_val[var_id] = var_value;
    }
  }

  printf("%d evidence instances\n", *p_num_inst);
#ifdef VERBOSE
  
  for (i=0; i<*p_num_inst; i++)
  {
    Instance_t * p_inst;
    int v;
    
    p_inst = (*pp_inst) + i;
    
    printf("Instance %d:", i);
    
    for (v=0; v<p_graph->graph_num_vars; v++)
      if (p_inst->inst_p_is_obs[v])
        printf(" %d => %d ", v, p_inst->inst_p_val[v]);
    
    printf("\n");
  }
  
#endif /* VERBOSE */
  fclose(fp);
}

void parse_mpe(char * mpe_fname, Graph_t * p_graph,
               int * p_num_inst, Instance_t ** pp_inst)
{
  FILE * fp;
  char buffer[LINEMAX];
  int i;
  
  fp = fopen(mpe_fname, "r");
  
  /* first read the type of file */
  PARSE_LINE(1, fscanf(fp, "%s\n", buffer),
             "Error: expecting exactly 1 string on line 1\n");

  if (strncmp(buffer, "MPE", 3))
  {
    fprintf(stderr, "Error: expecting file with MPE\n");
    exit(1);
  }

  /* read the number of evidence instances */
  PARSE_LINE(1, fscanf(fp, "%d\n", p_num_inst),
             "Error: expecting the number of instances on line 1\n");

  if (*p_num_inst <= 0)
  {
    fprintf(stderr, "Error: unexpected number of instances %d, "
            "expecting >= 1\n", *p_num_inst);
    exit(1);
  }
  
  /* allocate memory for the instances */
  *pp_inst = (Instance_t *) calloc(*p_num_inst, sizeof(**pp_inst));
  
  /* initialize and read each instance */
  for (i=0; i<*p_num_inst; i++)
  {
    Instance_t * p_inst;
    int inst_num_vars;
    int v;
    
    p_inst = (*pp_inst) + i;
    
    p_inst->inst_p_is_obs = (int *)calloc(p_graph->graph_num_vars,
                                          sizeof(*p_inst->inst_p_is_obs));
    
    p_inst->inst_p_val = (int *)calloc(p_graph->graph_num_vars,
                                       sizeof(*p_inst->inst_p_val));
  
    /* read the number of variables in the instance */
    PARSE_LINE(1, fscanf(fp, "%d ", &inst_num_vars),
               "Error: expecting the number of variables in instance\n");
  
    if (inst_num_vars != p_graph->graph_num_vars)
    {
      fprintf(stderr, "Expecting %d variables for instance %d, got %d\n",
              p_graph->graph_num_vars, i+1, inst_num_vars);
      exit(1);
    }
    
    for (v=0; v<inst_num_vars; v++)
    {
      int var_value;

      PARSE_LINE(1, fscanf(fp, "%d ", &var_value),
                 "Error: expecting a variable value\n");

      p_inst->inst_p_val[v] = var_value;
    }
  }

  printf("Read %d instances\n", *p_num_inst);
#ifdef VERBOSE
  
  for (i=0; i<*p_num_inst; i++)
  {
    Instance_t * p_inst;
    int v;
    
    p_inst = (*pp_inst) + i;
    
    printf("Instance %d:", i);
    
    for (v=0; v<p_graph->graph_num_vars; v++)
      printf(" %d", p_inst->inst_p_val[v]);
    
    printf("\n");
  }
  
#endif /* VERBOSE */
  fclose(fp);
}
